package com.youlai.boot.core.tenant.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.boot.common.exception.BusinessException;
import com.youlai.boot.core.security.util.SecurityUtils;
import com.youlai.boot.core.tenant.context.TenantContextHolder;
import com.youlai.boot.core.tenant.mapper.TenantApplicationMapper;
import com.youlai.boot.core.tenant.model.entity.Tenant;
import com.youlai.boot.core.tenant.model.entity.TenantApplication;
import com.youlai.boot.core.tenant.model.entity.TenantDatasource;
import com.youlai.boot.core.tenant.model.enums.TenantApplicationStatus;
import com.youlai.boot.core.tenant.model.form.TenantApplicationForm;
import com.youlai.boot.core.tenant.model.form.TenantApplicationReviewForm;
import com.youlai.boot.core.tenant.model.form.TenantDatabaseConfigForm;
import com.youlai.boot.core.tenant.model.query.TenantApplicationPageQuery;
import com.youlai.boot.core.tenant.model.vo.TenantApplicationVO;
import com.youlai.boot.core.tenant.service.GlobalUserService;
import com.youlai.boot.core.tenant.service.TenantApplicationService;
import com.youlai.boot.core.tenant.service.TenantDatasourceService;
import com.youlai.boot.core.tenant.service.TenantService;
import com.youlai.boot.system.model.entity.User;
import com.youlai.boot.system.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.time.LocalDateTime;

/**
 * 租户申请服务实现类
 *
 * @author haoxr
 * @since 3.0.0
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class TenantApplicationServiceImpl extends ServiceImpl<TenantApplicationMapper, TenantApplication>
        implements TenantApplicationService {

    private final TenantService tenantService;
    private final TenantDatasourceService tenantDatasourceService;
    private final UserService userService;
    private final GlobalUserService globalUserService;
    private final PasswordEncoder passwordEncoder;
    private final DataSource dataSource;
    private final DefaultDataSourceCreator dataSourceCreator;

    @Override
    public Page<TenantApplicationVO> listPagedApplications(TenantApplicationPageQuery query) {
        Page<TenantApplicationVO> page = new Page<>(query.getPageNum(), query.getPageSize());
        return baseMapper.listPagedApplications(page, query);
    }

    @Override
    public TenantApplicationVO getApplicationDetail(Long id) {
        TenantApplication application = this.getById(id);
        Assert.notNull(application, "租户申请不存在");

        TenantApplicationVO vo = new TenantApplicationVO();
        BeanUtil.copyProperties(application, vo);

        // 设置状态描述
        vo.setStatusDesc(getStatusDesc(application.getStatus()));

        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submitApplication(TenantApplicationForm form) {
        // 1. 校验租户名称是否已存在
        LambdaQueryWrapper<TenantApplication> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TenantApplication::getTenantName, form.getTenantName())
                .eq(TenantApplication::getIsDeleted, 0);
        TenantApplication existApplication = this.getOne(wrapper);
        if (existApplication != null) {
            throw new BusinessException("该租户名称已被申请，请更换");
        }

        // 2. 校验申请人手机号是否有待审核的申请
        wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TenantApplication::getApplicantMobile, form.getApplicantMobile())
                .eq(TenantApplication::getStatus, TenantApplicationStatus.PENDING.getCode())
                .eq(TenantApplication::getIsDeleted, 0);
        long count = this.count(wrapper);
        if (count > 0) {
            throw new BusinessException("您有一个待审核的申请，请等待审核完成");
        }

        // 3. 创建申请记录
        TenantApplication application = new TenantApplication();
        BeanUtil.copyProperties(form, application);
        application.setStatus(TenantApplicationStatus.PENDING.getCode());
        application.setSubmitTime(LocalDateTime.now());
        application.setIsDeleted(0);

        boolean saved = this.save(application);
        log.info("租户申请提交成功：申请人={}, 租户名称={}, 申请ID={}",
                form.getApplicantName(), form.getTenantName(), application.getId());

        return saved;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean reviewApplication(TenantApplicationReviewForm form) {
        // 1. 获取申请记录
        TenantApplication application = this.getById(form.getId());
        Assert.notNull(application, "租户申请不存在");

        // 2. 校验申请状态
        if (!TenantApplicationStatus.PENDING.getCode().equals(application.getStatus())) {
            throw new BusinessException("该申请已审核，无法重复审核");
        }

        // 3. 更新审核信息
        application.setReviewTime(LocalDateTime.now());
        application.setReviewerId(SecurityUtils.getUserId());
        application.setReviewerName(SecurityUtils.getUsername());
        application.setReviewComment(form.getReviewComment());

        if (Boolean.TRUE.equals(form.getApproved())) {
            // 审核通过
            application.setStatus(TenantApplicationStatus.APPROVED.getCode());

            // 生成租户编码
            String tenantCode = form.getTenantCode();
            if (StrUtil.isBlank(tenantCode)) {
                tenantCode = generateTenantCode();
            }
            application.setTenantCode(tenantCode);

            log.info("租户申请审核通过：申请ID={}, 租户名称={}, 租户编码={}",
                    application.getId(), application.getTenantName(), tenantCode);
        } else {
            // 审核拒绝
            application.setStatus(TenantApplicationStatus.REJECTED.getCode());
            application.setRejectReason(form.getRejectReason());

            log.info("租户申请审核拒绝：申请ID={}, 租户名称={}, 拒绝原因={}",
                    application.getId(), application.getTenantName(), form.getRejectReason());
        }

        return this.updateById(application);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean configureTenantDatabase(TenantDatabaseConfigForm form) {
        // 1. 获取申请记录
        TenantApplication application = this.getById(form.getId());
        Assert.notNull(application, "租户申请不存在");

        // 2. 校验申请状态
        if (!TenantApplicationStatus.APPROVED.getCode().equals(application.getStatus())) {
            throw new BusinessException("该申请未审核通过，无法配置");
        }

        try {
            // 3. 在 sys_tenant 表创建租户记录
            Tenant tenant = new Tenant();
            tenant.setTenantName(application.getTenantName());
            tenant.setTenantCode(application.getTenantCode());
            tenant.setStatus(1);
            // 补充工厂和申请人信息
            tenant.setContactName(application.getApplicantName());
            tenant.setMobilePhone(application.getApplicantMobile());
            tenant.setEmail(application.getApplicantEmail());
            // 使用 tenantName 作为 factoryName，或者后续可以从 form 中获取
            tenant.setFactoryName(application.getTenantName());
            
            tenantService.save(tenant);
            Long tenantId = tenant.getId();
            log.info("租户记录创建成功：tenantId={}, tenantName={}", tenantId, tenant.getTenantName());

            // 4. 在 sys_tenant_datasource 表创建数据源配置
            TenantDatasource datasource = new TenantDatasource();
            datasource.setTenantId(tenantId);
            datasource.setTenantCode(tenant.getTenantCode());
            datasource.setDatasourceName(form.getDbName());
            datasource.setDatasourceType("MySQL");
            datasource.setDriverClassName(StrUtil.isNotBlank(form.getDbDriver()) ?
                    form.getDbDriver() : "com.mysql.cj.jdbc.Driver");
            
            String dbUrl = String.format("jdbc:mysql://%s:%d/%s?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true",
                    form.getDbHost(), form.getDbPort(), form.getDbName());
            datasource.setUrl(dbUrl);
            datasource.setUsername(form.getDbUsername());
            datasource.setPassword(form.getDbPassword());
            datasource.setStatus(1);
            datasource.setIsPrimary(1);
            
            tenantDatasourceService.save(datasource);
            log.info("租户数据源配置创建成功：tenantId={}, dbName={}", tenantId, form.getDbName());

            // 5. 动态添加数据源
            addDynamicDataSource(datasource);

            // 6. 设置租户上下文，准备在租户数据库创建管理员
            TenantContextHolder.setTenantId(tenantId);

            try {
                // 7. 在租户数据库创建超级管理员
                User adminUser = new User();
                adminUser.setUsername(form.getAdminUsername());
                adminUser.setPassword(passwordEncoder.encode(form.getAdminPassword()));
                adminUser.setMobile(form.getAdminMobile());
                adminUser.setEmail(form.getAdminEmail());
                adminUser.setNickname(application.getApplicantName());
                adminUser.setStatus(1);

                userService.save(adminUser);
                log.info("租户超级管理员创建成功：userId={}, username={}", adminUser.getId(), adminUser.getUsername());

                // 8. 在全局用户表创建映射
                globalUserService.createOrUpdateGlobalUser(
                        application.getApplicantOpenId(),  // OpenID
                        null,                               // UnionID
                        form.getAdminMobile(),             // 手机号
                        tenantId,                          // 租户ID
                        adminUser.getId(),                 // 用户ID
                        adminUser.getUsername(),           // 用户名
                        adminUser.getNickname()            // 昵称
                );
                log.info("全局用户映射创建成功");

                // 9. 更新申请记录
                application.setTenantId(tenantId);
                application.setDbName(form.getDbName());
                application.setDbHost(form.getDbHost());
                application.setDbPort(form.getDbPort());
                application.setDbUsername(form.getDbUsername());
                application.setDbPassword(form.getDbPassword());
                application.setDbDriver(datasource.getDriverClassName());
                application.setDbInitialized(1);
                application.setAdminUsername(form.getAdminUsername());
                application.setAdminPassword(passwordEncoder.encode(form.getAdminPassword()));
                application.setAdminMobile(form.getAdminMobile());
                application.setAdminEmail(form.getAdminEmail());
                application.setAdminCreated(1);
                application.setStatus(TenantApplicationStatus.COMPLETED.getCode());
                application.setConfigTime(LocalDateTime.now());
                application.setConfigBy(SecurityUtils.getUserId());
                application.setConfigComment(form.getConfigComment());

                this.updateById(application);

                log.info("租户入驻配置完成：申请ID={}, 租户ID={}, 租户名称={}",
                        application.getId(), tenantId, application.getTenantName());

                return true;

            } finally {
                // 清理租户上下文
                TenantContextHolder.clear();
            }

        } catch (Exception e) {
            log.error("租户入驻配置失败", e);
            throw new BusinessException("租户配置失败：" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteApplication(Long id) {
        TenantApplication application = this.getById(id);
        Assert.notNull(application, "租户申请不存在");

        // 只有待审核或审核拒绝的申请才能删除
        if (TenantApplicationStatus.APPROVED.getCode().equals(application.getStatus()) ||
            TenantApplicationStatus.COMPLETED.getCode().equals(application.getStatus())) {
            throw new BusinessException("该申请已审核通过或已完成，无法删除");
        }

        application.setIsDeleted(1);
        return this.updateById(application);
    }

    /**
     * 生成租户编码
     */
    private String generateTenantCode() {
        return "TENANT_" + IdUtil.getSnowflakeNextIdStr();
    }

    /**
     * 动态添加数据源
     */
    private void addDynamicDataSource(TenantDatasource datasource) {
        try {
            DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;

            // 创建数据源配置
            DataSourceProperty property = new DataSourceProperty();
            property.setPoolName(datasource.getDatasourceName() + "-pool");
            property.setDriverClassName(datasource.getDriverClassName());
            property.setUrl(datasource.getUrl());
            property.setUsername(datasource.getUsername());
            property.setPassword(datasource.getPassword());

            // Hikari 连接池配置
            property.setLazy(true);
            property.getHikari().setMinimumIdle(3);
            property.getHikari().setMaximumPoolSize(20);
            property.getHikari().setConnectionTimeout(30000L);
            property.getHikari().setIdleTimeout(600000L);
            property.getHikari().setMaxLifetime(1800000L);

            // 创建并添加数据源
            // 使用 TenantId 作为 Key
            javax.sql.DataSource tenantDataSource = dataSourceCreator.createDataSource(property);
            ds.addDataSource(String.valueOf(datasource.getTenantId()), tenantDataSource);

            log.info("动态数据源添加成功：tenantId={}, dbName={}", datasource.getTenantId(), datasource.getDatasourceName());

        } catch (Exception e) {
            log.error("动态数据源添加失败：tenantId={}", datasource.getTenantId(), e);
            throw new BusinessException("数据源配置失败：" + e.getMessage());
        }
    }

    /**
     * 获取状态描述
     */
    private String getStatusDesc(String status) {
        if (StrUtil.isBlank(status)) {
            return "";
        }
        for (TenantApplicationStatus statusEnum : TenantApplicationStatus.values()) {
            if (statusEnum.getCode().equals(status)) {
                return statusEnum.getDesc();
            }
        }
        return "";
    }
}
